/*******************************************************************************
 * Copyright (c) 2012-2016 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.ide.actions;

import com.google.common.base.Optional;
import com.google.gwt.core.client.Callback;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;

import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.callback.CallbackPromiseHelper.Call;
import org.eclipse.che.api.promises.client.js.JsPromiseError;
import org.eclipse.che.api.promises.client.js.Promises;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.PromisableAction;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.event.ActivePartChangedEvent;
import org.eclipse.che.ide.api.event.ActivePartChangedHandler;
import org.eclipse.che.ide.api.event.FileEvent;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.resources.File;
import org.eclipse.che.ide.util.loging.Log;

import static org.eclipse.che.api.promises.client.callback.CallbackPromiseHelper.createFromCallback;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;

/**
 * TODO maybe rename it to factory open file?
 * @author Sergii Leschenko
 * @author Vlad Zhukovskyi
 */
@Singleton
public class OpenFileAction extends Action implements PromisableAction {

    /** ID of the parameter to specify file path to open. */
    public static final String FILE_PARAM_ID = "file";

    private final EventBus                 eventBus;
    private final CoreLocalizationConstant localization;
    private final NotificationManager      notificationManager;
    private final AppContext               appContext;

    private Callback<Void, Throwable> actionCompletedCallback;

    @Inject
    public OpenFileAction(EventBus eventBus,
                          CoreLocalizationConstant localization,
                          NotificationManager notificationManager,
                          AppContext appContext) {
        this.eventBus = eventBus;
        this.localization = localization;
        this.notificationManager = notificationManager;
        this.appContext = appContext;
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        if (event.getParameters() == null) {
            Log.error(getClass(), localization.canNotOpenFileWithoutParams());
            return;
        }

        final String pathToOpen = event.getParameters().get(FILE_PARAM_ID);
        if (pathToOpen == null) {
            Log.error(getClass(), localization.fileToOpenIsNotSpecified());
            return;
        }

        appContext.getWorkspaceRoot().getFile(pathToOpen).then(new Operation<Optional<File>>() {
            @Override
            public void apply(Optional<File> optionalFile) throws OperationException {
                if (optionalFile.isPresent()) {
                    if (actionCompletedCallback != null) {
                        actionCompletedCallback.onSuccess(null);
                    }

                    eventBus.fireEvent(FileEvent.createOpenFileEvent(optionalFile.get()));
                } else {
                    if (actionCompletedCallback != null) {
                        actionCompletedCallback.onFailure(null);
                    }

                    notificationManager.notify(localization.unableOpenResource(pathToOpen), FAIL, FLOAT_MODE);
                }
            }
        });
    }

    @Override
    public Promise<Void> promise(final ActionEvent actionEvent) {
        if (actionEvent.getParameters() == null) {
            return Promises.reject(JsPromiseError.create(localization.canNotOpenFileWithoutParams()));
        }

        final String pathToOpen = actionEvent.getParameters().get(FILE_PARAM_ID);
        if (pathToOpen == null) {
            return Promises.reject(JsPromiseError.create(localization.fileToOpenIsNotSpecified()));
        }

        final Call<Void, Throwable> call = new Call<Void, Throwable>() {
            HandlerRegistration handlerRegistration;

            @Override
            public void makeCall(final Callback<Void, Throwable> callback) {
                actionCompletedCallback = callback;
                handlerRegistration = eventBus.addHandler(ActivePartChangedEvent.TYPE, new ActivePartChangedHandler() {
                    @Override
                    public void onActivePartChanged(ActivePartChangedEvent event) {
                        if (event.getActivePart() instanceof EditorPartPresenter) {
                            EditorPartPresenter editor = (EditorPartPresenter)event.getActivePart();
                            handlerRegistration.removeHandler();
                            if ((pathToOpen).equals(editor.getEditorInput().getFile().getPath())) {
                                callback.onSuccess(null);
                            }
                        }
                    }
                });
                actionPerformed(actionEvent);
            }
        };

        return createFromCallback(call);
    }
}
